package com.immediatus.engine.util;

import com.immediatus.contracts.IUpdateHandler;
import com.immediatus.util.Pool;
import com.immediatus.util.PoolItem;

import java.util.ArrayList;


public abstract class PoolUpdateHandler<T extends PoolItem> implements IUpdateHandler{
    private final Pool<T> _pool;
    private final ArrayList<T> _scheduledPoolItems = new ArrayList<T>();

    public PoolUpdateHandler(){
        this._pool = new Pool<T>(){
            @Override
            protected T onAllocatePoolItem(){
                return PoolUpdateHandler.this.onAllocatePoolItem();
            }
        };
    }

    public PoolUpdateHandler(final int size_){
        this._pool = new Pool<T>(size_){
            @Override
            protected T onAllocatePoolItem(){
                return PoolUpdateHandler.this.onAllocatePoolItem();
            }
        };
    }

    public PoolUpdateHandler(final int size_, final int growth_){
        this._pool = new Pool<T>(size_, growth_){
            @Override
            protected T onAllocatePoolItem(){
                return PoolUpdateHandler.this.onAllocatePoolItem();
            }
        };
    }

    protected abstract T onAllocatePoolItem();

    protected abstract void onHandlePoolItem(final T item_);

    @Override
    public void onUpdate(final float secondsElapsed_){
        final ArrayList<T> scheduledPoolItems = this._scheduledPoolItems;

        synchronized (this._scheduledPoolItems){
            final int count = scheduledPoolItems.size();

            if (count > 0){
                final Pool<T> pool = this._pool;
                T item;

                for (T scheduledPoolItem : scheduledPoolItems){
                    item = scheduledPoolItem;
                    this.onHandlePoolItem(item);
                    pool.recyclePoolItem(item);
                }

                scheduledPoolItems.clear();
            }
        }
    }

    @Override
    public void reset(){
        final ArrayList<T> scheduledPoolItems = this._scheduledPoolItems;
        synchronized (this._scheduledPoolItems){
            final int count = scheduledPoolItems.size();

            final Pool<T> pool = this._pool;
            for (int i = count - 1; i >= 0; i--){
                pool.recyclePoolItem(scheduledPoolItems.get(i));
            }

            scheduledPoolItems.clear();
        }
    }

    public T obtainPoolItem(){
        return this._pool.obtainPoolItem();
    }

    public void postPoolItem(final T item_){
        synchronized (this._scheduledPoolItems){
            if (item_ == null){
                throw new IllegalArgumentException("PoolItem already recycled!");
            } else if (!this._pool.ownsPoolItem(item_)){
                throw new IllegalArgumentException("PoolItem from another pool!");
            }

            this._scheduledPoolItems.add(item_);
        }
    }
}
